using System;
using UnityEngine;

namespace GGPhysics
{
	[Serializable]
	public class PhysicsBody
	{
		[Serializable]
		public struct Transform
		{
			public Vector3 position;

			public Quaternion rotation;

			public void SetIdentityTransform()
			{
				position = Vector3.zero;
				rotation = Quaternion.identity;
			}

			public Vector3 TransformLocalToWorld(Vector3 pos)
			{
				return position + rotation * pos;
			}

			public void SetUnityTransform(UnityEngine.Transform posTrans, UnityEngine.Transform rotTrans)
			{
				position = posTrans.position;
				rotation = rotTrans.rotation;
			}

			public void Set(Transform trans)
			{
				position = trans.position;
				rotation = trans.rotation;
			}

			public static Transform Lerp(Transform a, Transform b, float t)
			{
				Transform result = default(Transform);
				result.position = Vector3.Lerp(a.position, b.position, t);
				result.rotation = Quaternion.Lerp(a.rotation, b.rotation, t);
				return result;
			}
		}

		public struct MotionParams
		{
			public Vector3 velocity;

			public Vector3 angularVelocity;

			public Vector3 velocityInUnity
			{
				get
				{
					Vector3 result = velocity;
					result.z *= -1f;
					return result;
				}
			}

			public Vector3 angularVelocityInUnity
			{
				get
				{
					Vector3 result = angularVelocity;
					result.x *= -1f;
					return result;
				}
			}

			public bool isMoving
			{
				get
				{
					return velocity != Vector3.zero || angularVelocity != Vector3.zero;
				}
			}

			public void Stop()
			{
				velocity = Vector3.zero;
				angularVelocity = Vector3.zero;
			}

			public void Set(MotionParams motion)
			{
				velocity = motion.velocity;
				angularVelocity = motion.angularVelocity;
			}
		}

		public enum PhysicsBodyType
		{
			StaticBody = 0,
			DynamicBody = 1
		}

		public struct TransformAndMotion
		{
			public MotionParams motion;

			public Transform transform;
		}

		public PhysicsBodyType type;

		public string tag;

		public object userData;

		[NonSerialized]
		public bool isActive = true;

		[NonSerialized]
		public Shape shape;

		public PhysicsBodyDefinition physicsDefinitions = new PhysicsBodyDefinition();

		[NonSerialized]
		public MotionParams myMotion = default(MotionParams);

		[NonSerialized]
		public Transform currentTransform = default(Transform);

		public bool isRolling
		{
			get
			{
				float magnitude = myMotion.velocity.magnitude;
				Vector3 normalized = myMotion.velocity.normalized;
				Vector3 rhs = Vector3.Cross(myMotion.angularVelocity, shape.GetDistanceToSurface(Vector3.down) * Vector3.down);
				float num = Vector3.Dot(normalized, rhs);
				return num < 0f && Mathf.Abs(magnitude + num) < 0.01f;
			}
		}

		public PhysicsBody()
		{
			currentTransform.SetIdentityTransform();
		}

		public static float Restitution(PhysicsBody a, PhysicsBody b)
		{
			return PhysicsBodyDefinition.Restitution(a.physicsDefinitions, b.physicsDefinitions);
		}

		public static float DynamicFriction(PhysicsBody a, PhysicsBody b)
		{
			return PhysicsBodyDefinition.DynamicFriction(a.physicsDefinitions, b.physicsDefinitions);
		}

		public static float DynamicFriction(PhysicsBody a, PhysicsBody b, float v)
		{
			return PhysicsBodyDefinition.DynamicFriction(a.physicsDefinitions, b.physicsDefinitions, v);
		}

		public PhysicsBody Clone()
		{
			PhysicsBody physicsBody = new PhysicsBody();
			physicsBody.currentTransform = currentTransform;
			physicsBody.type = type;
			physicsBody.myMotion = myMotion;
			physicsBody.physicsDefinitions = physicsDefinitions;
			physicsBody.shape = shape;
			physicsBody.tag = tag;
			physicsBody.userData = userData;
			return physicsBody;
		}

		public T GetUserData<T>() where T : class
		{
			return userData as T;
		}

		public T GetShape<T>() where T : Shape
		{
			return shape as T;
		}

		public void SetUnityTransform(UnityEngine.Transform posTrans, UnityEngine.Transform rotTrans)
		{
			currentTransform.SetUnityTransform(posTrans, rotTrans);
		}

		public virtual Transform TransformAfterDelta(float deltaTime, float integrationTime, PhysicsWorld.PhysicsConstants worldConstants)
		{
			if (integrationTime == 0f)
			{
				return TransformAfterDelta(deltaTime);
			}
			return TransformAndMotionAfterDelta(deltaTime, integrationTime, worldConstants).transform;
		}

		public virtual Transform TransformAfterDelta(float deltaTime)
		{
			if (deltaTime == 0f)
			{
				return currentTransform;
			}
			if (!myMotion.isMoving)
			{
				return currentTransform;
			}
			Vector3 vector = myMotion.velocity * deltaTime;
			Transform result = default(Transform);
			result.Set(currentTransform);
			result.position += vector;
			Vector3 vector2 = myMotion.angularVelocity * deltaTime;
			float magnitude = vector2.magnitude;
			Vector3 normalized = vector2.normalized;
			Quaternion quaternion = new Quaternion(Mathf.Sin(magnitude / 2f) * normalized.x, Mathf.Sin(magnitude / 2f) * normalized.y, Mathf.Sin(magnitude / 2f) * normalized.z, Mathf.Cos(magnitude / 2f));
			result.rotation = quaternion * result.rotation;
			return result;
		}

		public virtual Transform TransformAfterDelta(float deltaTime, Transform currentTransform, MotionParams myMotion)
		{
			if (deltaTime == 0f)
			{
				return currentTransform;
			}
			if (!myMotion.isMoving)
			{
				return currentTransform;
			}
			Vector3 vector = myMotion.velocity * deltaTime;
			Transform result = default(Transform);
			result.Set(currentTransform);
			result.position += vector;
			Vector3 vector2 = myMotion.angularVelocity * deltaTime;
			float magnitude = vector2.magnitude;
			Vector3 normalized = vector2.normalized;
			Quaternion quaternion = new Quaternion(Mathf.Sin(magnitude / 2f) * normalized.x, Mathf.Sin(magnitude / 2f) * normalized.y, Mathf.Sin(magnitude / 2f) * normalized.z, Mathf.Cos(magnitude / 2f));
			result.rotation = quaternion * result.rotation;
			return result;
		}

		public virtual void AdvanceMotion(float deltaTime, PhysicsWorld.PhysicsConstants worldConstants, ref MotionParams myMotion)
		{
			if (deltaTime == 0f || !myMotion.isMoving)
			{
				return;
			}
			Vector3 angularVelocity = myMotion.angularVelocity;
			Vector3 vector = myMotion.velocity.OnGround();
			Vector3 normalized = vector.normalized;
			float magnitude = vector.magnitude;
			Vector3 vector2 = Vector3.Cross(myMotion.angularVelocity.OnGround(), shape.GetDistanceToSurface(Vector3.down) * Vector3.down);
			Vector3 vector3 = vector + vector2;
			float magnitude2 = vector3.magnitude;
			Vector3 normalized2 = vector3.normalized;
			float dynamicFrictionCoefficient = worldConstants.floorPhysicsDefinitions.dynamicFrictionCoefficient;
			float num = Mathf.Abs(worldConstants.gravity.y);
			float distanceToSurface = shape.GetDistanceToSurface(Vector3.down);
			bool flag = magnitude2 <= 0.001f;
			if (!flag)
			{
				float a = magnitude2 / (num * deltaTime);
				dynamicFrictionCoefficient = MathEx.Min(a, dynamicFrictionCoefficient);
				Vector3 vector4 = (myMotion.velocity = vector - normalized2 * dynamicFrictionCoefficient * num * deltaTime);
				float num2 = dynamicFrictionCoefficient * num * deltaTime * shape.GetDistanceToSurface(Vector3.down) / shape.MomentOfInertiaWithoutMass();
				Vector3 vector5 = angularVelocity.OnGround() - Vector3.Cross(normalized2, Vector3.up) * num2;
				vector2 = Vector3.Cross(vector5, shape.GetDistanceToSurface(Vector3.down) * Vector3.down);
				Vector3 rhs = vector4 + vector2;
				float num3 = Vector3.Dot(normalized2, rhs);
				if (num3 <= 0f)
				{
					vector5 = Vector3.Cross(vector4 / distanceToSurface, Vector3.down);
					flag = true;
				}
				myMotion.angularVelocity = vector5;
			}
			if (flag)
			{
				float num4 = MathEx.Max(0f, magnitude - worldConstants.floorPhysicsDefinitions.rollingFrictionCoefficient * num * deltaTime);
				Vector3 vector6 = normalized * num4;
				Vector3 angularVelocity2 = Vector3.zero;
				if (num4 > 0f)
				{
					angularVelocity2 = Vector3.Cross(vector6 / distanceToSurface, Vector3.down);
				}
				myMotion.velocity = vector6;
				myMotion.angularVelocity = angularVelocity2;
			}
			myMotion.angularVelocity.y = Mathf.Sign(angularVelocity.y) * MathEx.Max(0f, Mathf.Abs(angularVelocity.y) - physicsDefinitions.rotationAroundYAxisDragDeceleration * deltaTime);
		}

		public virtual void AdvanceMotion(float deltaTime, PhysicsWorld.PhysicsConstants worldConstants)
		{
			if (deltaTime == 0f || !myMotion.isMoving)
			{
				return;
			}
			Vector3 angularVelocity = myMotion.angularVelocity;
			Vector3 vector = myMotion.velocity.OnGround();
			Vector3 normalized = vector.normalized;
			float magnitude = vector.magnitude;
			Vector3 vector2 = Vector3.Cross(myMotion.angularVelocity.OnGround(), shape.GetDistanceToSurface(Vector3.down) * Vector3.down);
			Vector3 vector3 = vector + vector2;
			float magnitude2 = vector3.magnitude;
			Vector3 normalized2 = vector3.normalized;
			float dynamicFrictionCoefficient = worldConstants.floorPhysicsDefinitions.dynamicFrictionCoefficient;
			float num = Mathf.Abs(worldConstants.gravity.y);
			float distanceToSurface = shape.GetDistanceToSurface(Vector3.down);
			bool flag = magnitude2 <= 0.001f;
			if (!flag)
			{
				float a = magnitude2 / (num * deltaTime);
				dynamicFrictionCoefficient = MathEx.Min(a, dynamicFrictionCoefficient);
				Vector3 vector4 = vector - normalized2 * dynamicFrictionCoefficient * num * deltaTime;
				myMotion.velocity = vector4;
				float num2 = dynamicFrictionCoefficient * num * deltaTime * shape.GetDistanceToSurface(Vector3.down) / shape.MomentOfInertiaWithoutMass();
				Vector3 vector5 = angularVelocity.OnGround() - Vector3.Cross(normalized2, Vector3.up) * num2;
				vector2 = Vector3.Cross(vector5, shape.GetDistanceToSurface(Vector3.down) * Vector3.down);
				Vector3 rhs = vector4 + vector2;
				float num3 = Vector3.Dot(normalized2, rhs);
				if (num3 <= 0f)
				{
					vector5 = Vector3.Cross(vector4 / distanceToSurface, Vector3.down);
					flag = true;
				}
				myMotion.angularVelocity = vector5;
			}
			if (flag)
			{
				float num4 = MathEx.Max(0f, magnitude - worldConstants.floorPhysicsDefinitions.rollingFrictionCoefficient * num * deltaTime);
				Vector3 vector6 = normalized * num4;
				Vector3 angularVelocity2 = Vector3.zero;
				if (num4 > 0f)
				{
					angularVelocity2 = Vector3.Cross(vector6 / distanceToSurface, Vector3.down);
				}
				myMotion.velocity = vector6;
				myMotion.angularVelocity = angularVelocity2;
			}
			myMotion.angularVelocity.y = Mathf.Sign(angularVelocity.y) * MathEx.Max(0f, Mathf.Abs(angularVelocity.y) - physicsDefinitions.rotationAroundYAxisDragDeceleration * deltaTime);
		}

		public virtual void AdvanceTransformAndMotion(float deltaTime, float integrationDelta, PhysicsWorld.PhysicsConstants worldConstants)
		{
			if (deltaTime != 0f && myMotion.isMoving)
			{
				TransformAndMotion transformAndMotion = TransformAndMotionAfterDelta(deltaTime, integrationDelta, worldConstants);
				currentTransform.Set(transformAndMotion.transform);
				myMotion.Set(transformAndMotion.motion);
			}
		}

		public virtual TransformAndMotion TransformAndMotionAfterDelta(float deltaTime, float integrationDelta, PhysicsWorld.PhysicsConstants worldConstants)
		{
			TransformAndMotion result = default(TransformAndMotion);
			result.motion = myMotion;
			result.transform = currentTransform;
			if (deltaTime == 0f)
			{
				return result;
			}
			if (!myMotion.isMoving)
			{
				return result;
			}
			MotionParams motion = myMotion;
			float num = deltaTime;
			Transform transform = currentTransform;
			while (num > 0f)
			{
				float deltaTime2 = MathEx.Min(num, integrationDelta);
				transform = TransformAfterDelta(deltaTime2, transform, motion);
				AdvanceMotion(deltaTime2, worldConstants, ref motion);
				num -= integrationDelta;
			}
			result.motion = motion;
			result.transform = transform;
			return result;
		}

		public virtual void AdvanceTransform(float deltaTime)
		{
			if (deltaTime != 0f && myMotion.isMoving)
			{
				currentTransform.Set(TransformAfterDelta(deltaTime));
			}
		}

		public void ApplyForce(MotionParams newMotion)
		{
			myMotion.angularVelocity = newMotion.angularVelocity;
			myMotion.velocity = newMotion.velocity;
		}

		public void ApplyForce(Vector3 force, Vector3 radiusVectGlobal, float time, float torqueMultiplierXZ = 1f, float torqueMultiplierY = 1f, float topspinForceMultiplier = 1f, float backspinForceMultiplier = 1f, float topspinLinearAccelerationMultiplier = 1f, float backspinLinearAccelerationMultiplier = 1f, float minLinearAccelerationMultiplier = 1f)
		{
			if (type == PhysicsBodyType.StaticBody)
			{
				return;
			}
			Vector3 vector = radiusVectGlobal.normalized * shape.GetDistanceToSurface(radiusVectGlobal);
			Vector3 vector2 = Vector3.zero;
			if (vector != Vector3.zero)
			{
				float num = shape.MomentOfInertia();
				Vector3 vector3 = Vector3.Cross(vector, force);
				vector3.x *= torqueMultiplierXZ;
				vector3.y *= torqueMultiplierY;
				vector3.z *= torqueMultiplierXZ;
				Vector3 lhs = vector3 * time / num;
				vector2 = Vector3.Cross(lhs, shape.GetDistanceToSurface(Vector3.down) * Vector3.down);
				if (Vector3.Dot(vector2.normalized, force.normalized) >= 0f)
				{
					vector3.x *= backspinForceMultiplier;
					vector3.z *= backspinForceMultiplier;
				}
				else
				{
					vector3.x *= topspinForceMultiplier;
					vector3.z *= topspinForceMultiplier;
				}
				myMotion.angularVelocity += vector3 * time / num;
			}
			Vector3 vector4 = ((!(vector != Vector3.zero)) ? Vector3.zero : Vector3.Cross(vector, force));
			vector4.y = 0f;
			float num2 = vector.magnitude * force.magnitude;
			Vector3 vector5 = Vector3.Cross(myMotion.angularVelocity, shape.GetDistanceToSurface(Vector3.down) * Vector3.down);
			bool flag = Vector3.Dot(vector2.normalized, force.normalized) > 0f;
			bool flag2 = Vector3.Dot(vector2.normalized, force.normalized) < 0f;
			Vector3 vector6 = ((!flag2) ? Vector3.zero : (vector5 * shape.mass / time));
			Vector3 vector7 = ((!flag2) ? force : (MathEx.Max(vector6.magnitude, force.magnitude * (1f - vector4.magnitude / num2)) * force.normalized));
			if (flag)
			{
				vector7 *= backspinLinearAccelerationMultiplier;
			}
			else if (flag2)
			{
				vector7 *= topspinLinearAccelerationMultiplier;
			}
			myMotion.velocity += vector7.OnGround() * time / shape.mass;
		}

		public void Stop()
		{
			myMotion.Stop();
		}
	}
}
